/*=+--+=#=+--     Unmanned Aerial System Management Software      --+=#=+--+=#*\
|          Copyright (C) 2011 Regents of the University of Colorado.           |
|                             All Rights Reserved.                             |

     This program is free software: you can redistribute it and/or modify
     it under the terms of the GNU General Public License version 2 as
     published by the Free Software Foundation.

     This program is distributed in the hope that it will be useful,
     but WITHOUT ANY WARRANTY; without even the implied warranty of
     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     GNU General Public License for more details.

     You should have received a copy of the GNU General Public License
     along with this program.  If not, see <http://www.gnu.org/licenses/>.

            Jack Elston                       Cory Dixon                        
|           elstonj@colorado.edu              dixonc@colorado.edu              |
|                                                                              |
\*=+--+=#=+--                 --+=#=+--+=#=+--                    --+=#=+--+=#*/

#include <iostream>
#include <stdlib.h>
#include <stdio.h>

#include "nodeIconManager.h"
#include "vcConfig.h"

using namespace std;

/*<---Global Variables---->*/
extern GuiStruct guiStr;
extern String nodeToTrack;

static GtkToggleActionEntry icon_toggle_entries[] = 
{
	//------------- Layers Menu --------------------//
	{ "ToggleTrack", GTK_STOCK_EXECUTE,     // name, stock id
		"_Track Node", NULL,      // label, accelerator
		"Toggle tracking of this icon in map window on/off",   // tooltip
		G_CALLBACK(NodeIconManager::toggle_track_cb),
		0 // initial state
	},
};

NodeIconManager::NodeIconManager()
{
	resetFromZoom = false;

	menu         = NULL;
	action_group = NULL;
	menu_manager = NULL;

}

NodeIconManager::~NodeIconManager()
{
}

void NodeIconManager::init(GPSMap *map)
{
	LayerManager::init(map);
	layerG = (GooCanvasItem*)gps_map->getLayerG(GPSMap::ICON_LAYER);

	// Create new GtkActionGroup and add entries 
	action_group = gtk_action_group_new ("IconActions");
	gtk_action_group_add_toggle_actions (action_group, icon_toggle_entries, G_N_ELEMENTS(icon_toggle_entries), this);

	// insert action group into menu_manager
	menu_manager = gps_map->get_MenuManager();
	gtk_ui_manager_insert_action_group (menu_manager, action_group, GPSMap::ICON_LAYER);

}

void NodeIconManager::addNodes() 
{
	Client * a_client;
	int i = 0;
	
	while( (a_client = guiStr.shm_ptr->clients.getClient(i)) != NULL) {
		if(a_client->state.init && a_client->state.gps && !a_client->state.gui) {
			NodeTypes_t nodeType = UNKNOWN_NODE;

			if (a_client->capabilities.find(CMD_GPS))
				nodeType = MNR;
			if (a_client->capabilities.find(CMD_GUI_CMD))
				nodeType = GS;
			if (a_client->capabilities.find(CMD_FLIGHT_COMPUTER))
				nodeType = MAV;
			if (a_client->capabilities.find(CMD_AUTOPILOT))
				nodeType = SAV;

			mapIcons[i].client_ind = i;
			mapIcons[i].scale      = 1.0;
			mapIcons[i].filled     = false;
			mapIcons[i].old_x      = -1;
			mapIcons[i].old_y      = -1;

			mapIcons[i].item = GOO_CANVAS_ITEM(goo_canvas_group_new(layerG,
						"x", 0.0,
						"y", 0.0,
						NULL));


			if( nodeType == SAV ) {
				mapIcons[i].type = SAV_g;
				create_sav_icon(i);
			}
			else if( nodeType == MAV ) {
				mapIcons[i].type = MAV_g;
				create_mav_icon(i);
			}
			else if( nodeType == MNR ) {
				mapIcons[i].type = MNR_g;
				create_mnr_icon(i);
			}
			else if( nodeType == GS )  {
				mapIcons[i].type = GS_g;
				create_gs_icon(i);
			} else {
				cout << "NodeIconManager::addNodes -- could not find node type: client=" 
				     << a_client->name << " nodeType=" << nodeType << endl;

				return;
			}

			// now make sure all layers are shown
			gps_map->addLayer( a_client->name, mapIcons[i].item, (GooCanvasItem*)GPSMap::ICON_LAYER);
			gps_map->setLayerListShown( mapIcons[i].item, true);
			gps_map->addLayer( String("Icon"), mapIcons[i].icon, mapIcons[i].item);
			gps_map->setLayerListShown( mapIcons[i].icon, true);
			gps_map->addLayer( String("Text"), mapIcons[i].text, mapIcons[i].item);
			gps_map->setLayerListShown( mapIcons[i].text, true);

			// make sure parent manager layer is shown
			gps_map->toggleLayerShown(GPSMap::ICON_LAYER, true);

			// hide the object since a GPS fix is not valid yet
			g_object_set (G_OBJECT(mapIcons[i].item), "visibility", GOO_CANVAS_ITEM_INVISIBLE, NULL);

			// setup signal for button press
			g_signal_connect (G_OBJECT (mapIcons[i].item), "button-press-event", 
					G_CALLBACK (NodeIconManager::button_press_event_cb), this);

			/*--------- set state as now having icon and being drawn ----------*/
			a_client->state.gui = 1;
		}

		 /*--------- check next client ----------*/
		i ++;
	}
}

void NodeIconManager::updateMap()
{
	Client * a_client = NULL;
	double x = NAN, y = NAN, yaw = 0.0;

	int i = 0;
	int numI = 0;
	//-- see if there are any nodes to track
	if( !resetFromZoom ) {
		while( (a_client = guiStr.shm_ptr->clients.getClient(i)) != NULL) 
		{
			if(a_client->state.gui && mapIcons[i].track ) 
			{
				if( IsNaN(x) ) x = a_client->telemetry.gps.pos.lon;
				else x += a_client->telemetry.gps.pos.lon;

				if( IsNaN(y) ) y = a_client->telemetry.gps.pos.lat;
				else y += a_client->telemetry.gps.pos.lat;

				numI++;
			}
			i++;
		}

		//-- move map
		if( !IsNaN(x) && !IsNaN(y) )
			gps_map->moveToGeo(x / (float)numI, y / (float)numI);
	}

	//-- now update icons
	x = 0.0;
	y = 0.0;
	i = 0;
	while( (a_client = guiStr.shm_ptr->clients.getClient(i)) != NULL) 
	{
		if(a_client->state.gui) 
		{
			if( mapIcons[i].old_x == -1 && mapIcons[i].old_y == -1 )
				g_object_set (G_OBJECT(mapIcons[i].item), "visibility", GOO_CANVAS_ITEM_VISIBLE, NULL);

			x = a_client->telemetry.gps.pos.lon;
			y = a_client->telemetry.gps.pos.lat;
			gps_map->geoToLayer(GPSMap::ICON_LAYER, x, y);

			if(mapIcons[i].type == MAV_g || mapIcons[i].type == SAV_g) {
				if (a_client->capabilities.find(CMD_TELEM))
					yaw = (180.0 / M_PI) * (a_client->telemetry.angles.yaw / 1000.0);
				if (a_client->capabilities.find(CMD_GPS_TELEM))
					yaw = a_client->telemetry.gps.track;
			}
			else{
				if( a_client->telemetry.angles.yaw )
					cout << "!!! something set the yaw angle of a non UA client" << endl;

				// we don't have a yaw angle for a non UA vehicle, and we don't 
				// want to use the track angle to rotate the icons
				//yaw = a_client->telemetry.gps.track;
				yaw = 0;
			}

			if( yaw < -180.0 ) yaw += 360.0;
			else if( yaw > 180.0 ) yaw -= 360.0;

			switch( mapIcons[i].type ) {
				case SAV_g:
					update_sav_text(i);
					break;
				case MAV_g:
					update_mav_text(i);
					break;
				case MNR_g:
					update_mnr_text(i);
					break;
				case GS_g:
					update_gs_text(i);
					break;
				default:
					break;
			}

			//cout << "client " << i << ":" << endl 
				//<< "\tlat=" << a_client->telemetry.gps.pos.lat 
				//<< " lon=" << a_client->telemetry.gps.pos.lon 
				//<< " track=" << a_client->telemetry.gps.track << endl
				//<< "\tx=" << x << " y=" << y 
				//<< " old_x=" << mapIcons[i].old_x << " old_y=" << mapIcons[i].old_y << endl;

			if( !gps_map->animateMotion() || resetFromZoom || 
					mapIcons[i].old_x == -1 || mapIcons[i].old_y == -1 ) {
				if( mapIcons[i].item )
					goo_canvas_item_set_simple_transform( mapIcons[i].item, 
							x, y, mapIcons[i].scale, 0.0);
				if( mapIcons[i].icon )
					goo_canvas_item_set_simple_transform( mapIcons[i].icon, 
							0.0, 0.0, 1.0, yaw);
			} else {
				//cout<< "animation" << endl;
				if( mapIcons[i].item )
					goo_canvas_item_animate(mapIcons[i].item,
							x, y,
							mapIcons[i].scale, 0.0, TRUE, 
							ANIMATION_TIME, ANIMATION_STEPS,
							GOO_CANVAS_ANIMATE_FREEZE);

				// because the way the animaition works, we need to send a delta heading angle
				// instead of the abosolute, thus get the current yaw angle drawn and calc a delta
				// yaw to new angle
				double scale, rot;
				goo_canvas_item_get_simple_transform(mapIcons[i].icon, &x, &y, &scale, &rot);
				yaw = yaw - rot;

				if( yaw < -180.0 ) yaw += 360.0;
				else if( yaw > 180.0 ) yaw -= 360.0;

				if( mapIcons[i].icon )
					goo_canvas_item_animate(mapIcons[i].icon,
							0.0, 0.0,
							1.0, yaw, FALSE, 
							ANIMATION_TIME, ANIMATION_STEPS,
							GOO_CANVAS_ANIMATE_FREEZE);
			}

			//-- set operational color
			//cout << "Node: " << a_client->name 
				//<< " OBType=" << (int)a_client->telemetry.navigation.OBtype 
				//<< " OBMode=" << (int)a_client->telemetry.navigation.OBmode 
				//<< endl;

			if( a_client->telemetry.navigation.OBtype > OB_NONE ) {
				if( a_client->telemetry.navigation.OBmode == OB_INIT )
					g_object_set(G_OBJECT(mapIcons[i].icon), "stroke-color", "yellow", NULL);
				else if( a_client->telemetry.navigation.OBmode == OB_ERROR )
					g_object_set(G_OBJECT(mapIcons[i].icon), "stroke-color", "red", NULL);
				else if( a_client->telemetry.navigation.OBmode > OB_INIT )
					g_object_set(G_OBJECT(mapIcons[i].icon), "stroke-color", "magenta", NULL);
			} else
				g_object_set(G_OBJECT(mapIcons[i].icon), "stroke-color", mapIcons[i].color_str, NULL);

			//-- set color if timed out
			if( a_client->state.timed_out && !mapIcons[i].timed_out ) {
				mapIcons[i].timed_out = true;
				g_object_set(G_OBJECT(mapIcons[i].icon), "stroke-color", "red", NULL);
				g_object_set(G_OBJECT(mapIcons[i].text), "fill-color", "red", NULL);
				if( mapIcons[i].filled )
					g_object_set(G_OBJECT(mapIcons[i].icon), "fill-color", "red", NULL);
			} else if( !a_client->state.timed_out && mapIcons[i].timed_out ) {
				mapIcons[i].timed_out = false;
				g_object_set(G_OBJECT(mapIcons[i].text), "fill-color", "black", NULL);
				g_object_set(G_OBJECT(mapIcons[i].icon), "stroke-color", mapIcons[i].color_str, NULL);
				if( mapIcons[i].filled )
					g_object_set(G_OBJECT(mapIcons[i].icon), "fill-color", mapIcons[i].color_str, NULL);
			}

			//-- set color if failure or errors
			if( a_client->telemetry.failure.aeroTermination 
				|| a_client->telemetry.failure.flightTermination 
				//|| a_client->telemetry.failure.deadman 
				|| a_client->telemetry.failure.manual_engineKill 
				|| a_client->telemetry.failure.auto_engineKill 
				|| a_client->telemetry.failure.operator_engineKill )
			{
				
				if(mapIcons[i].termination == 1) {
					mapIcons[i].termination = 2;
					g_object_set(G_OBJECT(mapIcons[i].icon), "stroke-color", mapIcons[i].color_str, NULL);
				} else {
					mapIcons[i].termination = 1;
					g_object_set(G_OBJECT(mapIcons[i].icon), "stroke-color", "red", NULL);
				}
			} else {
				mapIcons[i].termination = 0;

				if( a_client->telemetry.limits.altitudeLow || a_client->telemetry.limits.altitudeHigh 
					|| a_client->telemetry.limits.IASLow || a_client->telemetry.limits.IASHigh 
					|| a_client->telemetry.limits.rollHigh || a_client->telemetry.limits.pitchHigh
					|| a_client->telemetry.limits.RPMLow || a_client->telemetry.limits.RPMHigh 
					|| a_client->telemetry.failure.gpsTimeout 
					|| a_client->telemetry.failure.commTimeout )
				{

					if(mapIcons[i].failure == 1) {
						mapIcons[i].failure = 2;
						g_object_set(G_OBJECT(mapIcons[i].icon), "stroke-color", mapIcons[i].color_str, NULL);
					} else {
						mapIcons[i].failure = 1;
						g_object_set(G_OBJECT(mapIcons[i].icon), "stroke-color", "yellow", NULL);
					}

				} else
					mapIcons[i].failure = 0;

			}

			mapIcons[i].old_x = x;
			mapIcons[i].old_y = y;
		}
		i++;
	}
}

#if 0
	char *filename = "drawing.svg";
	static GdkPixbuf *pixbuf = NULL;
	GError *error = NULL;

	printf("loading image ... "); fflush(stdout);
	pixbuf = gdk_pixbuf_new_from_file (filename, &error);
	//image = gtk_image_new_from_file (filename);
	if( error ) {
		/* This code shows off error handling. You can just use
		 * gtk_image_new_from_file() instead if you don't want to report
		 * errors to the user. If the file doesn't load when using
		 * gtk_image_new_from_file(), a "missing image" icon will
		 * be displayed instead.
		 */
		GtkWidget *dialog;

		dialog = gtk_message_dialog_new (GTK_WINDOW (window->window),
				GTK_DIALOG_DESTROY_WITH_PARENT,
				GTK_MESSAGE_ERROR,
				GTK_BUTTONS_CLOSE,
				"ERROR: %s",
				error->message);
		g_error_free (error);

		g_signal_connect (dialog, "response",
				G_CALLBACK (gtk_widget_destroy), NULL);

		gtk_widget_show (dialog);
	}

	printf("loaded\n");
	if( pixbuf ) {
		item = goo_canvas_item_new (layerG,
				goo_canvas_pixbuf_get_type(),
				"pixbuf", pixbuf,
				"x", 0.0,
				"y", 0.0,
				"width", (double) gdk_pixbuf_get_width(pixbuf),
				"height", (double)gdk_pixbuf_get_height(pixbuf),
				"width_set", TRUE,
				"height_set", TRUE,
				NULL);
		setup_item(item);
		g_object_unref(pixbuf);
	}
#endif

void NodeIconManager::create_gs_icon(unsigned int ind)
{
	strcpy( mapIcons[ind].color_str, "green");

	mapIcons[ind].icon = goo_canvas_ellipse_new( mapIcons[ind].item,
			0.0, 0.0,
			6.0, 6.0,
			"stroke-color", mapIcons[ind].color_str,
			"line-width", 3.0,
			NULL);

	if( mapIcons[ind].filled )
		g_object_set(G_OBJECT(mapIcons[ind].icon), "fill-color", mapIcons[ind].color_str, NULL);

	update_gs_text(ind);

	int ai = ind % N_ANCHORS;
	mapIcons[ind].text =  goo_canvas_text_new (mapIcons[ind].item, mapIcons[ind].text_str, 
			Anchors[ai].x, Anchors[ai].y, -1,
			Anchors[ai].anchor,
			"font", "Sans 8px",
			"fill-color", "black",
			NULL);
}

void NodeIconManager::create_mnr_icon(unsigned int ind)
{
	strcpy( mapIcons[ind].color_str, "blue");
	mapIcons[ind].icon = goo_canvas_polyline_new (mapIcons[ind].item, FALSE, 8,
      -7.0, 0.0,
      -7.0, 4.0,
       7.0, 4.0,
       7.0, 0.0,
       3.0, -8.0,
      -3.0, -8.0,
      -7.0, 0.0,
       7.0, 0.0,
			"stroke-color", mapIcons[ind].color_str,
			"line-width", 2.0,
			NULL);

	if( mapIcons[ind].filled )
		g_object_set(G_OBJECT(mapIcons[ind].icon), "fill-color", mapIcons[ind].color_str, NULL);

	update_mnr_text(ind);

	int ai = ind % N_ANCHORS;
	mapIcons[ind].text =  goo_canvas_text_new (mapIcons[ind].item, mapIcons[ind].text_str, 
			Anchors[ai].x, Anchors[ai].y, -1,
			Anchors[ai].anchor,
			"font", "Sans 8px",
			"fill-color", "black",
			NULL);
}

void NodeIconManager::create_mav_icon(unsigned int ind)
{
	strcpy( mapIcons[ind].color_str, "green");
	
	mapIcons[ind].icon = goo_canvas_polyline_new (mapIcons[ind].item, FALSE, 5,
			0.0, 0.0,
			-10.0, 15.0,
			10.0, 15.0,
			0.0, 0.0,
			0.0, -15.0,
			"stroke-color", mapIcons[ind].color_str,
			"line-width", 2.0,
			"end-arrow", TRUE,
			"arrow-tip-length", 4.0,
			"arrow-length", 5.0,
			"arrow-width", 5.0,
			NULL);

	if( mapIcons[ind].filled )
		g_object_set(G_OBJECT(mapIcons[ind].icon), "fill-color", mapIcons[ind].color_str, NULL);

	update_mav_text(ind);

	int ai = ind % N_ANCHORS;
	mapIcons[ind].text =  goo_canvas_text_new (mapIcons[ind].item, mapIcons[ind].text_str,
			Anchors[ai].x, Anchors[ai].y, -1,
			Anchors[ai].anchor,
			"font", "Sans 8px",
			"fill-color", "black",
			NULL);
}

void NodeIconManager::create_sav_icon(unsigned int ind)
{
	strcpy( mapIcons[ind].color_str, "orange");

	//FIXME hack for ghosts
	Client * a_client = guiStr.shm_ptr->clients.getClient(ind);
	if(a_client != NULL)
	{
		if(!strcmp(a_client->name.c_str(),"Roomba0"))
			strcpy( mapIcons[ind].color_str, "yellow");
		if(!strcmp(a_client->name.c_str(),"Roomba1"))
			strcpy( mapIcons[ind].color_str, "red");
		if(!strcmp(a_client->name.c_str(),"Roomba2"))
			strcpy( mapIcons[ind].color_str, "orange");
		if(!strcmp(a_client->name.c_str(),"Roomba3"))
			strcpy( mapIcons[ind].color_str, "cyan");
		if(!strcmp(a_client->name.c_str(),"Roomba4"))
			strcpy( mapIcons[ind].color_str, "magenta");
	}
	//end FIXME

	mapIcons[ind].icon = goo_canvas_polyline_new (mapIcons[ind].item, FALSE, 5,
			0.0, 0.0,
			-15.0, 20.0,
			15.0, 20.0,
			0.0, 0.0,
			0.0, -20.0,
			"stroke-color", mapIcons[ind].color_str,
			"line-width", 2.0,
			"end-arrow", TRUE,
			"arrow-tip-length", 5.0,
			"arrow-length", 6.0,
			"arrow-width", 6.0,
			NULL);

	if( mapIcons[ind].filled )
		g_object_set(G_OBJECT(mapIcons[ind].icon), "fill-color", mapIcons[ind].color_str, NULL);

	update_sav_text(ind);

	int ai = ind % N_ANCHORS;
	mapIcons[ind].text =  goo_canvas_text_new (mapIcons[ind].item, mapIcons[ind].text_str, 
			Anchors[ai].x, Anchors[ai].y, -1,
			Anchors[ai].anchor,
			"font", "Sans 8px",
			"fill-color", "black",
			NULL);
}

bool NodeIconManager::update_gs_text(unsigned int ind) 
{
	Client * a_client = guiStr.shm_ptr->clients.getClient(ind);
	if( a_client == NULL )
		return false;

	sprintf( mapIcons[ind].text_str, "%s", a_client->name.c_str() );
	if( mapIcons[ind].text )
		g_object_set(G_OBJECT(mapIcons[ind].text), "text", mapIcons[ind].text_str, NULL);

	return true;
}

bool NodeIconManager::update_mnr_text(unsigned int ind) 
{
	Client * a_client = guiStr.shm_ptr->clients.getClient(ind);
	if( a_client == NULL )
		return false;

	float range = NAN;

	// use gps feed of an MNR
	Client * me = guiStr.shm_ptr->clients.getClient(0);
	if( nodeToTrack != "" && me->state.gps == 0 ) {
		Client * my_client = guiStr.shm_ptr->clients.getClient(nodeToTrack.c_str());
		if(my_client && me){
			me->telemetry.gps = my_client->telemetry.gps;
			range = my_client->rangeTo(a_client);
			range = meterToLengthU(range, guiStr.config.units);
		}
	} else {
		range = guiStr.shm_ptr->clients.getRange(ind), 
		range = meterToLengthU(range, guiStr.config.units);
	}

	float sog = a_client->telemetry.gps.sog;
	//sog = mpsToSpeedU(sog, guiStr.config.units);
	sog = ktsToSpeedU(sog, guiStr.config.units);

	int n = sprintf(mapIcons[ind].text_str, "%s\n",a_client->name.c_str());

	n += sprintf( mapIcons[ind].text_str + n, "%.1f %s\n%.1f %s", 
			range, lengthU_str(guiStr.config.units, range),
			sog, speedU_str(guiStr.config.units) );

	if( mapIcons[ind].text )
		g_object_set(G_OBJECT(mapIcons[ind].text), "text", mapIcons[ind].text_str, NULL);

	return true;
}

bool NodeIconManager::update_mav_text(unsigned int ind) 
{
	Client * a_client = guiStr.shm_ptr->clients.getClient(ind);
	if( a_client == NULL )
		return false;

	//sprintf( mapIcons[ind].text_str, "%s\n%.1f\n%4.0f\n%4.1f\n%i", 
		//a_client->name.c_str(), guiStr.shm_ptr->clients.getRange(ind), a_client->telemetry.gps.pos.alt, 
		//a_client->telemetry.gps.sog,  a_client->telemetry.navigation.waypt);

	float range =	guiStr.shm_ptr->clients.getRange(ind);
	range = meterToLengthU(range, guiStr.config.units);
	float alt = a_client->telemetry.gps.pos.alt;
	alt = meterToLengthU(alt, guiStr.config.units);
	float sog = a_client->telemetry.gps.sog;
	sog = mpsToSpeedU(sog, guiStr.config.units);

	int n = sprintf(mapIcons[ind].text_str, "%s\n", a_client->name.c_str());

	n += sprintf( mapIcons[ind].text_str + n, "%.1f %s\n%.1f %s\n%.1f %s", 
			range, lengthU_str(guiStr.config.units, range),
			alt, lengthU_str(guiStr.config.units),
			sog, speedU_str(guiStr.config.units) );

	n += sprintf( mapIcons[ind].text_str + n, "\n%i", a_client->telemetry.navigation.waypt);

	if( mapIcons[ind].text )
		g_object_set(G_OBJECT(mapIcons[ind].text), "text", mapIcons[ind].text_str, NULL);

	return true;
}

bool NodeIconManager::update_sav_text(unsigned int ind) 
{
	Client * a_client = guiStr.shm_ptr->clients.getClient(ind);
	if( a_client == NULL )
		return false;

	//sprintf( mapIcons[ind].text_str, "%s\n%0.1f\n%4.0f\n%4.1f\n%i", 
		//a_client->name.c_str(), guiStr.shm_ptr->clients.getRange(ind), a_client->telemetry.gps.pos.alt, 
		//a_client->telemetry.gps.sog,  a_client->telemetry.navigation.waypt);

	float range =	guiStr.shm_ptr->clients.getRange(ind);
	range = meterToLengthU(range, guiStr.config.units);
	float alt = a_client->telemetry.gps.pos.alt;
	alt = meterToLengthU(alt, guiStr.config.units);
	float sog = a_client->telemetry.gps.sog;
	sog = mpsToSpeedU(sog, guiStr.config.units);

	int n = sprintf(mapIcons[ind].text_str, "%s\n", a_client->name.c_str());

	n += sprintf( mapIcons[ind].text_str + n, "%.1f %s\n%.1f %s\n%.1f %s", 
			range, lengthU_str(guiStr.config.units, range),
			alt, lengthU_str(guiStr.config.units),
			sog, speedU_str(guiStr.config.units) );

	n += sprintf( mapIcons[ind].text_str + n, "\n%i", a_client->telemetry.navigation.waypt);


	if( mapIcons[ind].text )
		g_object_set(G_OBJECT(mapIcons[ind].text), "text", mapIcons[ind].text_str, NULL);

	return true;
}

void NodeIconManager::translate_from_zoomChange(double zoom_factor)
{
	int i = 0;
	double x, y, scale, rot;

	//cout << " NodeIconManager::translate_from_zoomChange " << endl;

	// for icon group, its children are icons, consisting of different sub-children
	// such as text box and icon itself
	for( i=0; i < goo_canvas_item_get_n_children(layerG); i++ ) {

		// the next child is the point box with the text
		GooCanvasItem* child = goo_canvas_item_get_child(layerG, i);
		if( child ) {
			// stop any animation to get real position
			goo_canvas_item_stop_animation(child);

			goo_canvas_item_get_simple_transform(child, &x, &y, &scale, &rot);
			//cout << "x=" << x << " y=" << y << " scale=" << scale <<  " rot=" << rot << endl;
			goo_canvas_item_set_simple_transform( child, zoom_factor * x, zoom_factor * y, scale, 0.0);
		}
	}
	resetFromZoom = true;
	updateMap();
	resetFromZoom = false;
}

bool NodeIconManager::button_press_event_cb (GooCanvasItem *item, GooCanvasItem *target_item, GdkEventButton *event, NodeIconManager *me)
{
/*
	cout << "NodeIconManager::button_press_event_cb: " 
		<< " me=" << me
		<< " event->type=" << event->type 
		<<"  event->x=" <<  event->x
		<< " event->y=" <<  event->y << endl;
*/

	if( me == NULL )
		return FALSE;

	int iconInd = -1;

	// find the point for wich this signal came
	for( int clientInd = 0; clientInd < MAX_CLIENTS+1; clientInd++ ) {
		if( me->mapIcons[clientInd].item == item ) {
			iconInd = clientInd;
			clientInd = MAX_CLIENTS+1;
		}
	}

	if( iconInd == -1 ) { 
		cout << " - did not find valid point group for mouse button" << endl;
		return FALSE;
	} 

	switch (event->type) {
		case GDK_2BUTTON_PRESS:
			break;

		case GDK_BUTTON_PRESS: 
			switch(event->button) 
			{
				case LEFT_MOUSE:
					break;

				case RIGHT_MOUSE:
					// set active icon and create menu
					me->activeIconInd = iconInd;
					me->create_control_window(event);
					return TRUE;
					break;

				case MIDDLE_MOUSE:
					break;

				default:
					printf("Unknown mouse button value\n");
					break;
			} 
		default:
			break;
	}

	// Returning FALSE propagates the event to parent items;
	// returning TRUE ends event propagation. 
	return FALSE;
}

int NodeIconManager::create_control_window( GdkEventButton *event)
{
	// Get the popup menu
	if( !menu )
		menu = gtk_ui_manager_get_widget (menu_manager, "/IconPopup");
	gtk_menu_popup (GTK_MENU(menu), NULL, NULL, NULL, NULL, event->button, event->time);

	return TRUE;
}

int NodeIconManager::toggle_track_cb(GtkToggleAction *action, NodeIconManager *me)
{
	if( me == NULL )
		return FALSE;

	if( gtk_toggle_action_get_active(action) ) {
		me->setTrack(me->activeIconInd, 1);
	} else {
		me->setTrack(me->activeIconInd, 0);
	}

	return TRUE;
}

void NodeIconManager::clearAllTrack() 
{ 
	//cout << "NodeIconManager::clearAllTrack()" << endl;

	for(int ind=0; ind < MAX_CLIENTS+1; ind++) 
		mapIcons[ind].track = false; 

	GtkToggleAction* action = GTK_TOGGLE_ACTION(gtk_action_group_get_action(action_group, "ToggleTrack"));
	if(action) gtk_toggle_action_set_active(action,0);
	else cout << "NodeIconManager::clearAllTrack -- did not find action button to clear" << endl;
} 

bool NodeIconManager::isTracking() 
{ 
	for(int ind=0; ind < MAX_CLIENTS; ind++) 
		if(mapIcons[ind].track) 
			return true; 

	return false; 
}
